import const

from threading import Lock

class Block:
	"""A specific block."""
	def __init__(self, id, props):
		self.id	= id
		self.props = props
		self.owner = None

	def setOwner(self, owner):
		self.owner = owner

	def isOwned(self):
		return self.owner is not None

	def __eq__(self, other):
		return self.id == other.id

	def __neq__(self, other):
		return self.id != other.id

	def __str__(self):
		return "Block" + str(self.id)


oo = float("infinity")

class Restriction:
	"""Provides a way to express constraints on property values."""
	def __init__(self, name, reference, sign):
		self.name = name
		self.reference = reference 
		self.sign = sign

	def __str__(self):
		return self.name + " " + self.sign + " " + str(self.reference)

	def fits(self, block):
		"""Checks if the given block fits this restriction."""
		if block.props[self.name] is None:
			return True
		return {
			const.GT:	lambda x: x > self.reference,
			const.GE:	lambda x: x >= self.reference,
			const.EQ:	lambda x: x == self.reference,
			const.LT:	lambda x: x < self.reference,
			const.LE:	lambda x: x <= self.reference
		}[self.sign](block.props[self.name])

	def makeInterval(self):
		if self.sign == const.GT:
			return Interval(self.reference, oo, False, False)
		if self.sign == const.GE:
			return Interval(self.reference, oo, True, False)
		if self.sign == const.EQ:
			return Interval(self.reference, self.reference, True, True)
		if self.sign == const.LE:
			return Interval(-oo, self.reference, False, True)
		if self.sign == const.LT:
			return Interval(-oo, self.reference, False, False)

	def merge(r1, r2):
		if r1.name == r2.name:
			if r1.sign == const.EQ and r2.sign == const.EQ:
				if r1.reference == r2.reference:
					return [r1]
				else:
					return None
			# make intervals
			i1 = r1.makeInterval()
			i2 = r2.makeInterval()
			i = Interval.intersect(i1, i2)
			if i is None:
				return None
			return i.makeRestrictions(r1.name)
		else:
			return [r1, r2]

class Interval:
	def __init__(self, lval=0, hval=0, lclosed=False, hclosed=False):
		self.lval = lval
		self.hval = hval
		self.lclosed = lclosed
		self.hclosed = hclosed

	def intersect(i1, i2):
		i = Interval()
		i.lval = max(i1.lval, i2.lval)
		i.hval = min(i1.hval, i2.hval)

		set_closed = lambda val, val1, val2, closed1, closed2:	\
						closed1 and closed2 if val1 == val2		\
						else closed1 if val == val1 else closed2

		i.lclosed = set_closed(i.lval, i1.lval, i2.lval, i1.lclosed, i2.lclosed)
		i.hclosed = set_closed(i.hval, i1.hval, i2.hval, i1.hclosed, i2.hclosed)

		if i.lval > i.hval:
			return None

		if i.lval == i.hval and not (i.lclosed and i.hclosed):
			return None

		return i

	def makeRestrictions(self, name):
		if self.lval == -oo:
			sign = const.LE if self.hclosed else const.LT
			return [Restriction(name, self.hval, sign)]
		elif self.hval == oo:
			sign = const.GE if self.lclosed else const.GT
			return [Restriction(name, self.lval, sign)]
		elif self.lval == self.hval:
			return [Restriction(name, self.lval, const.EQ)]
		else:
			lsign = const.GE if self.lclosed else const.GT
			hsign = const.LE if self.hclosed else const.LT
			return [Restriction(name, self.lval, lsign),
					Restriction(name, self.hval, hsign)] 
			
lock = Lock()

class Slot:
	"""A wildcard for a block, used to specify goals to agents."""
	def __init__(self, restrictions):
		self.restrictions = restrictions
	
	def __str__(self):
		ret = "{"
		for r in self.restrictions:
			ret += str(r) + ", " 
		if self.restrictions:
			ret = ret[0:-2]
		ret +=  "}"
		return ret

	def fits(self, block):
		for r in self.restrictions:
			if not r.fits(block):
				return False
		return True

	def merge(s1, s2):
		all_restr = s1.restrictions + s2.restrictions
		restr = []
		for r1 in all_restr:
			add = True
			for r2 in restr:
				r = Restriction.merge(r1, r2)
				if r is None:
					return None
				if len(r) == 1:
					restr.remove(r2)
					restr.extend(r)
					add = False
					break
			if add:
				restr.append(r1)
					
		ret = Slot(restr)
		return ret 

class Section:
	"""A stack of slots."""
	def __init__(self, slots):
		self.slots = slots
		self.shared = None
	
	def merge(sect1, sect2, slot1, slot2):

		pos1 = sect1.slots.index(slot1)
		pos2 = sect2.slots.index(slot2)
		delta1 = 0 if pos1 > pos2 else pos2 - pos1
		delta2 = 0 if pos2 > pos1 else pos1 - pos2
		l = max(pos1, pos2) + max(len(sect1.slots)-pos1, len(sect2.slots)-pos2)

		new_slots = []
		valid = lambda sect, index: index >= 0 and index < len(sect.slots)
		slot = lambda sect, i: sect.slots[i] if valid(sect, i) else Slot([])
		mSlot = lambda i: Slot.merge(slot(sect1,i-delta1),slot(sect2,i-delta2))
		map(lambda x: new_slots.append(mSlot(x)), range(l))

		if None in new_slots:
			return None
		return Section(new_slots)

def createBlock(id, color, weight, width, height):
		return Block(id, {const.COLOR:color, const.WEIGHT:weight,
						 	const.WIDTH:width, const.HEIGHT:height})
